/***************************************************************************
 *   Copyleft 2008-2009 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./vDeviceInfo.h"
#include "../../Exceptions/vFatalException.h"


namespace Vendetta {
	vDeviceInfo::vDeviceInfo(const vDeviceInfo& infos) {
		mValidOptions.Clear();
		mDevModes.Clear();
		mDevModes = infos.mDevModes;
		mValidOptions = infos.mValidOptions;
		mCurrentDevMode = infos.mCurrentDevMode;
	}

	vDeviceInfo& vDeviceInfo::operator=(const vDeviceInfo& infos) {
		if (this != &infos) {
			mValidOptions.Clear();
			mDevModes.Clear();
			mDevModes = infos.mDevModes;
			mValidOptions = infos.mValidOptions;
			mCurrentDevMode = infos.mCurrentDevMode;
		}

		return *this;
	}

	vBool vDeviceInfo::Init() {
		DEVMODE DevMode;
		memset(&DevMode, 0, sizeof(DevMode));
		DevMode.dmSize = sizeof(DEVMODE);
		vConfigOption opt;
		mCurrentDevMode = -1;

		for (vUInt32 i = 0; ::EnumDisplaySettings(vNull, i, &DevMode); ++i) {
			if (DevMode.dmBitsPerPel < 16 || DevMode.dmPelsHeight < 480 || DevMode.dmPelsWidth < DevMode.dmPelsHeight) {
				continue;
			}
			vStringStream ss;
			ss<<DevMode.dmPelsWidth<<" x "<<DevMode.dmPelsHeight;
			vString mode = vString(ss.str());
			mDevModes<<DevMode;
			if (!opt.AvailableOptions.Contains(mode)) {
				opt.AvailableOptions<<mode;
			}
			
		}
		if (opt.AvailableOptions.IsEmpty()) {
			return false;
		}

		opt.CurrentOption = opt.AvailableOptions[opt.AvailableOptions.Size() - 1];
		opt.IsReadOnly = false;
		mValidOptions.Insert(vString("VideoMode"), opt);

		opt.AvailableOptions.Clear();
		opt.AvailableOptions<<"OpenGL";
		opt.CurrentOption = opt.AvailableOptions[0];
		opt.IsReadOnly = true;
		mValidOptions.Insert("RenderSystem", opt);

		opt.AvailableOptions.Clear();
		opt.CurrentOption = "";
		opt.IsReadOnly = false;
		mValidOptions.Insert("Frequency", opt);

		opt.AvailableOptions.Clear();
		opt.CurrentOption = "";
		opt.IsReadOnly = false;
		mValidOptions.Insert("ColorDepth", opt);

		opt.AvailableOptions.Clear();
		opt.AvailableOptions<<"Yes"<<"No";
		opt.CurrentOption = opt.AvailableOptions[0];
		opt.IsReadOnly = false;
		mValidOptions.Insert("VSync", opt);

		opt.CurrentOption = opt.AvailableOptions[0];
		opt.IsReadOnly = false;
		mValidOptions.Insert("GPUGeometry", opt);

		opt.CurrentOption = opt.AvailableOptions[0];
		opt.IsReadOnly = false;
		mValidOptions.Insert("FullScreen", opt);

		return true;
	}

	vBool vDeviceInfo::Refresh() {
		mCurrentDevMode = -1;
		vConfigOption optVideo, *optFreq, *optColor;

		try {
			optVideo = mValidOptions["VideoMode"];
			optFreq = &mValidOptions["Frequency"];
			optColor = &mValidOptions["ColorDepth"];
		} catch (vException& e) {
			throw vFatalException("Fatal error while refreshing available display modes, because: %s In vDeviceInfo::Refresh.", e.ErrorMsg());
		}
		vUInt uiPos = optVideo.CurrentOption.Find('x');
		if (uiPos == vString::MaxPos) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Video Mode\". In vDeviceInfo::Refresh.");
		}
		vBool bOK = false;
		vUInt uiWidth = vStringConverter::ToUInt(optVideo.CurrentOption.SubString(0, uiPos), &bOK);
		if (!bOK) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Width\". In vDeviceInfo::Refresh.");
		}
		bOK = false;
		vUInt uiHeight = vStringConverter::ToUInt(optVideo.CurrentOption.SubString(uiPos + 1, vString::MaxPos), &bOK);
		if (!bOK) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Height\". In vDeviceInfo::Refresh.");
		}

		for(vUInt i = 0; i < mDevModes.Size(); ++i) {
			if (mDevModes[i].dmPelsWidth != uiWidth || mDevModes[i].dmPelsHeight != uiHeight) {
				continue;
			}
			vStringStream ss;
			ss<<vStringConverter::ToString(mDevModes[i].dmBitsPerPel)<<" bits";
			vString str = vString(ss.str());
			ss.str("");
			if (!optColor->AvailableOptions.Contains(str)) {
				optColor->AvailableOptions<<str;	
			}
			ss<<vStringConverter::ToString(mDevModes[i].dmDisplayFrequency)<<" Hz";
			str = vString(ss.str());
			if (!optFreq->AvailableOptions.Contains(str)) {
				optFreq->AvailableOptions<<str;
			}
		}
		if (optColor->AvailableOptions.IsEmpty()) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Color Depth\". In vDeviceInfo::Refresh.");
		}
		optColor->CurrentOption = optColor->AvailableOptions[optColor->AvailableOptions.Size() - 1];
		if (optFreq->AvailableOptions.IsEmpty()) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Frequency\". In vDeviceInfo::Refresh.");
		}
		optFreq->CurrentOption = optFreq->AvailableOptions[optFreq->AvailableOptions.Size() - 1];

		return true;
	}

	vBool vDeviceInfo::IsValid() {
		vConfigOption optVideo, optFreq, optColor;

		try {
			optVideo = mValidOptions["VideoMode"];
			optFreq = mValidOptions["Frequency"];
			optColor = mValidOptions["ColorDepth"];
		} catch (vException& e) {
			throw vFatalException("Fatal error while refreshing available display modes, because: %s In vDeviceInfo::ValidateVideoMode.", e.ErrorMsg());
		}
		vUInt uiPos = optVideo.CurrentOption.Find('x');
		if (uiPos == vString::MaxPos) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Video Mode\". In vDeviceInfo::ValidateVideoMode.");
		}
		vBool bOK = false;
		vUInt uiWidth = vStringConverter::ToUInt(optVideo.CurrentOption.SubString(0, uiPos), &bOK);
		if (!bOK) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Width\". In vDeviceInfo::ValidateVideoMode.");
		}
		bOK = false;
		vUInt uiHeight = vStringConverter::ToUInt(optVideo.CurrentOption.SubString(uiPos + 1, vString::MaxPos), &bOK);
		if (!bOK) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Height\". In vDeviceInfo::ValidateVideoMode.");
		}
		bOK = false;
		uiPos = optColor.CurrentOption.Find(' ');
		vUInt uiColorBit = vStringConverter::ToUInt(optColor.CurrentOption.SubString(0, uiPos), &bOK);
		if (!bOK) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Color Depth\". In vDeviceInfo::ValidateVideoMode.");
		}
		bOK = false;
		uiPos = optFreq.CurrentOption.Find(' ');
		vUInt uiFreq = vStringConverter::ToUInt(optFreq.CurrentOption.SubString(0, uiPos), &bOK);
		if (!bOK) {
			throw vFatalException("Fatal error while refreshing available display modes. Not valid \"Frequency\". In vDeviceInfo::ValidateVideoMode.");
		}
		for(vUInt i = 0; i < mDevModes.Size(); ++i) {
			if (mDevModes[i].dmPelsWidth == uiWidth && mDevModes[i].dmPelsHeight == uiHeight
				&& mDevModes[i].dmBitsPerPel == uiColorBit && mDevModes[i].dmDisplayFrequency == uiFreq) {
				mDevModes[i].dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
				mCurrentDevMode = i;

				return true;
			}
		}

		return false;
	}

	vBool vDeviceInfo::IsFullScreen() {
		vConfigOption fullMode;
		vBool bFullScreen, bOK;

		try {
			fullMode = mValidOptions["FullScreen"];
			bFullScreen = vStringConverter::ToBool(fullMode.CurrentOption, &bOK);
			if (bOK) {
				return bFullScreen;
			} else {
				return false;
			}
		} catch (vException&) {
			return false;
		}
	}

	const DEVMODE* vDeviceInfo::CurrentDevMode() const {
		if (mCurrentDevMode == -1) {
			return vNull;
		} else {
			return &mDevModes[mCurrentDevMode];
		}
	}
}